package com.ccteam.fluidmusic.fluidmusic.common.utils

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.ResultReceiver
import android.support.v4.media.MediaDescriptionCompat
import android.support.v4.media.MediaMetadataCompat
import com.ccteam.fluidmusic.fluidmusic.common.PlaylistManager
import com.ccteam.fluidmusic.fluidmusic.media.extensions.METADATA_KEY_QUEUE_ID
import com.ccteam.fluidmusic.fluidmusic.media.extensions.id
import com.ccteam.fluidmusic.fluidmusic.media.extensions.toMediaSource
import com.ccteam.fluidmusic.fluidmusic.media.library.BrowseTree
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.ControlDispatcher
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.Player.*
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
import com.google.android.exoplayer2.source.ConcatenatingMediaSource
import com.google.android.exoplayer2.upstream.DataSource
import com.orhanobut.logger.Logger
import kotlinx.coroutines.*

/**
 *
 * @author Xiaoc
 * @since 2021/1/14
 *
 * 播放列表编辑器
 */
class FluidMusicQueueEditor(
    private val defaultFactory: DataSource.Factory,
    private val browseTree: BrowseTree,
    private val mediaSource: ConcatenatingMediaSource,
    private val sourceFactory: DataSource.Factory,
    private val playlistStorage: PlaylistStorage
): MediaSessionConnector.QueueEditor, MediaSessionConnector.CommandReceiver {

    // 协程组件
    private val serviceJob = SupervisorJob()
    private val serviceScope = CoroutineScope(Dispatchers.Main + serviceJob)

    private val handler = QueueEditorHandler()

    override fun onAddQueueItem(player: Player, description: MediaDescriptionCompat) {
        var nowPlaying = false

        val addType = description.extras?.getInt(MediaIDHelper.PLAYLIST_ADD_TYPE) ?: run {
            Logger.d("当前未指定添加到播放列表行为类型，以添加到播放列表末尾类型为默认")
            MediaIDHelper.PLAYLIST_ADD_TYPE_LAST_PLAY
        }

        val index = when (addType) {
            MediaIDHelper.PLAYLIST_ADD_TYPE_NEXT_PLAY -> {
                // 如果获取下一首索引无效，则直接使用windowCount作为添加下一首的索引
                if(player.nextWindowIndex == C.INDEX_UNSET){
                    Logger.d("索引：下一首索引无效：定位到：${player.currentTimeline.windowCount}")
                    player.currentTimeline.windowCount
                } else {
                    Logger.d("索引：下一首索引有效：定位到：${player.nextWindowIndex}")
                    player.nextWindowIndex
                }
            }
            MediaIDHelper.PLAYLIST_ADD_TYPE_LAST_PLAY -> {
                player.currentTimeline.windowCount
            }
            else -> {
                nowPlaying = true
                player.currentWindowIndex
            }
        }

        description.extras?.getBoolean(MediaIDHelper.CACHE_PLAY)?.let { cachePlay ->
            if(cachePlay){
                description.extras?.getParcelable<MediaMetadataCompat>(MediaIDHelper.CACHE_PLAY_METADATA)?.let { metadata ->
                    // 这是来自搜索或直接播放的内容，直接使用windowCount作为添加下一首的索引，并在添加完成决定是否直接播放
                    handleAddQueueItem(player,metadata,index,
                        nowPlaying)
                    return
                }
            }
        }

        onAddQueueItem(player,description,index,nowPlaying)
    }

    /**
     * 添加播放内容到播放列表
     * 这里直接从 [BrowseTree] 中寻找，更便捷，因为 [BrowseTree] 中就存在所有能看到的媒体数据
     * 但前提 [description] 中需要提供当前MediaId和对应parentId，否则不做任何操作
     * 将description转为[MediaMetadataCompat]加入播放器和播放列表中
     */
    override fun onAddQueueItem(player: Player, description: MediaDescriptionCompat, index: Int) {
        onAddQueueItem(player,description,index,false)
    }

    private fun onAddQueueItem(player: Player,description: MediaDescriptionCompat,index: Int,nowPlaying: Boolean){
        val mediaId = description.mediaId ?: run {
            Logger.d("缺少添加播放列表的基本信息：mediaId")
            return
        }
        val parentId = description.extras?.getString(MediaIDHelper.PARENT_ID_TAG) ?: run {
            Logger.d("缺少添加播放列表的基本信息：parentId")
            return
        }
        serviceScope.launch {
            val metadata = getMediaFromBrowseTree(mediaId,parentId) ?: run{
                Logger.d("没有找到对应添加的歌曲内容：mediaId:$mediaId -- parentId:$parentId")
                return@launch
            }
            handleAddQueueItem(player,metadata,index,nowPlaying)
        }
    }

    private fun handleAddQueueItem(player: Player,metadata: MediaMetadataCompat,index: Int,nowPlaying: Boolean){
        serviceScope.launch {
            Logger.d("添加内容：$metadata -- 立即播放:$nowPlaying")
            PlaylistManager.add(index,metadata)

            /**
             * 添加该metadata到数据源中
             * 并完成后决定是否转到该项进行播放
             */
            mediaSource.addMediaSource(index,metadata.toMediaSource(sourceFactory,defaultFactory), handler){
                if(nowPlaying){
                    player.seekTo(index, C.TIME_UNSET)
                    player.playWhenReady = true
                }
            }

            handleSavePlaylist(PlaylistManager.getItems())
        }
    }

    /**
     * 从播放列表中移除某项
     *
     *  @param description
     *  由于传来的是该对象，而为了支持相同歌曲重复添加到播放列表，所以必须有一个extra包含处于播放列表的ID（与exoplayer的index一致）
     *  来告诉播放器该删除当前哪个位置的歌曲
     */
    override fun onRemoveQueueItem(player: Player, description: MediaDescriptionCompat) {
        val extra = description.extras ?: run {
            Logger.d("缺少删除播放列表的基本信息：METADATA_KEY_QUEUE_ID")
            return
        }
        val removeIndex = extra.getLong(METADATA_KEY_QUEUE_ID).toInt()
        serviceScope.launch {
            mediaSource.removeMediaSource(removeIndex)
            PlaylistManager.remove(removeIndex)
            handleSavePlaylist(PlaylistManager.getItems())
        }
    }

    override fun onCommand(
        player: Player,
        controlDispatcher: ControlDispatcher,
        command: String,
        extras: Bundle?,
        cb: ResultReceiver?
    ): Boolean {
        return false
    }

    fun close(){
        serviceJob.cancel()
    }

    private suspend fun handleSavePlaylist(playlist: List<MediaMetadataCompat>){
        playlistStorage.savePlaylist(playlist)
    }

    private suspend fun getMediaFromBrowseTree(mediaId: String,parentId: String): MediaMetadataCompat?{
        return withContext(Dispatchers.Default){
            val mediaList = browseTree.getMediaList(parentId)
            mediaList?.find {
                it.id == mediaId
            }
        }
    }

    class QueueEditorHandler: Handler(Looper.getMainLooper()){

    }
}